<?php

declare(strict_types=1);
/**
 * This file is part of habo_new_media.
 *
 * @link     https://www.berlen.cn/
 * @contact  tech@berlen.cn
 * @license  http://gitlab.rehou.cn/habo/habo_new_media/-/blob/master/README.md
 */

namespace app\service\notice\dingtalk;

use app\enum\DingTalkEnum;
use app\enum\DingTalkUrlEnum;
use app\enum\NoticeEnum;
use app\model\DingTalkUserModel;
use app\model\StaffModel;
use app\service\BaseService;
use app\service\notice\NoticeInterface;
use app\service\notice\NoticeLogTrait;
use proxy\Client;
use think\Exception;
use think\facade\Cache;

final class DingTalkService extends BaseService implements NoticeInterface
{
    use NoticeLogTrait;

    /**
     * @var string appKey
     */
    public const APP_KEY = 'dingzkoxv6gftgraiulz';

    /**
     * @var string appSecret
     */
    public const APP_SECRET = 'jEVNR2tV40mFP92QmRti94bFy5zbqjmltsLNRlrAgWIxF8PiH15IpOEtU8R__0i-';

    /**
     * @var array 请求参数
     */
    protected array $req = [];

    /**
     * @var array 应答参数
     */
    protected array $rep = [];

    /**
     * @var array 接收人
     */
    protected array $staffs = [];

    /**
     * @var int 通知类型
     */
    protected int $noticeType = NoticeEnum::NOTICE_TYPE_DING_TALK;

    /**
     * @var array 配置
     */
    private array $conf = [];

    /**
     * @var string token
     */
    private string $token = '';

    public function __construct(array $conf = [])
    {
        $this->setConf($conf);
        $this->setToken();
    }

    /**
     * 获取 tenant_access_token（企业自建应用）.
     * @return bool|mixed|string
     * @throws Exception
     */
    public function getAccessToken()
    {
        $redis = Cache::store('redis')->handler();
        $redisKey = DingTalkEnum::TOKEN_KEY;
        // 先判断redis是否还有
        $token = $redis->get($redisKey);
        if (! $token) {
            // 重新获取
            $params = [
                'appkey' => self::APP_KEY,
                'appsecret' => self::APP_SECRET,
            ];
            $client = new Client();
            $serviceName = DingTalkUrlEnum::getServiceName('dingTalk');
            $serviceUrl = DingTalkUrlEnum::getServiceUrl('token', 'get_access_token');
            $res = $client->get($serviceName, $serviceUrl, $params)->exec(Client::RETURN_TYPE);
            if ($res['errcode'] == 0) {
                $token = $res['access_token'];
                $expire = $res['expires_in'] - 120;
                $redis->set($redisKey, $token, $expire);
            } else {
                return false;
            }
        }
        return $token;
    }

    /**
     * 获取单个用户信息.
     * @param string $staffId 员工id
     * @return bool|mixed
     * @throws Exception
     */
    public function getUsersIdByPhone(string $staffId = '')
    {
        // 查询手机号
        $userId = (new DingTalkUserModel())->where('staff_id', $staffId)->value('user_id');
        if (! $userId) {
            // 查询手机号
            $mobile = (new StaffModel())->where('id', $staffId)->value('mobile');
            if (! $mobile) {
                return false;
            }
            $headers = self::getHeader();
            $params = ['mobile' => $mobile];
            $client = new Client();
            $serviceName = DingTalkUrlEnum::getServiceName('dingTalk');
            $serviceUrl = self::setTokenUrl(DingTalkUrlEnum::getServiceUrl('msg', 'user_mobile'));
            $res = $client->postJson($serviceName, $serviceUrl, $params)->setHeader($headers)->exec(Client::RETURN_TYPE);

            if ($res['errcode'] == 0) {
                $userId = $res['result']['userid'];
                $data = [
                    'staff_id' => $staffId,
                    'mobile' => $mobile,
                    'user_id' => $userId,
                ];
                // 存数据库
                DingTalkUserModel::create($data);
            } else {
                return false;
            }
        }
        return $userId;
    }

    /**
     * 发送单聊互动卡片.
     * @param string $userId 用户userid
     * @param string $cardCode 钉钉卡片代码
     * @throws Exception
     */
    public function SendCardMessages(string $userId = '', string $cardCode = ''): bool
    {
        $this->req = [
            'singleChatReceiver' => '{"user_id":"' . $userId . '"}',
            'cardBizId' => uniqid((string) time()),
            'cardTemplateId' => 'StandardCard',  // 卡片搭建平台模板ID，固定值填写为StandardCard。
            'robotCode' => self::APP_KEY,       // 机器人编码
            'cardData' => $cardCode,
        ];
        $newHeaders = self::getNewHeader();
        $client = new Client();
        $newServiceName = DingTalkUrlEnum::getServiceName('dingTalkApi');
        $serviceUrl = DingTalkUrlEnum::getServiceUrl('msg', 'send_cards_messages');
        $this->rep = $client->postJson($newServiceName, $serviceUrl, $this->req)->setHeader($newHeaders)->exec(Client::RETURN_TYPE);
        if (! $this->setNoticeLog()) {
            return false;
        }
        return $this->isSendSuccess() || self::setError($res['msg'] ?? '发送失败');
    }

    /**
     * 添加公共参数.
     * @param array $params 请求参数
     * @return array
     */
    public function getParam(array $params = []): array
    {
        $userId = $params['user_id'];
        $cardCode = $params['card_code'];
        return [
            'singleChatReceiver' => '{"user_id":"' . $userId . '"}',
            'cardBizId' => uniqid((string) time()),
            'cardTemplateId' => 'StandardCard',  // 卡片搭建平台模板ID，固定值填写为StandardCard。
            'robotCode' => self::APP_KEY,       // 机器人编码
            'cardData' => $cardCode,
        ];
    }

    /**
     * 设置配置.
     * @param array $conf 配置
     * @return bool
     */
    public function setConf(array $conf = []): bool
    {
        $this->conf = $conf;
        return true;
    }

    /**
     * 是否发送成功.
     */
    public function isSendSuccess(): bool
    {
        return isset($this->rep['processQueryKey']);
    }

    /**
     * 发送单聊互动卡片.
     * @param array $params 请求参数
     * @return bool
     */
    public function sendSingleMessage(array $params = []): bool
    {
        return true;
    }

    /**
     * 发送批量消息.
     * @param array $params 请求参数
     * @return bool
     * @throws Exception
     */
    public function sendBatchMessage(array $params = []): bool
    {
        $this->req = $this->getParam($params);
        $newHeaders = self::getNewHeader();
        $client = new Client();
        $serviceName = DingTalkUrlEnum::getServiceName('dingTalkApi');
        $serviceUrl = DingTalkUrlEnum::getServiceUrl('msg', 'send_cards_messages');
        $this->rep = $client->postJson($serviceName, $serviceUrl, $this->req)->setHeader($newHeaders)->exec(Client::RETURN_TYPE);
        if (! $this->setNoticeLog()) {
            return false;
        }
        return $this->isSendSuccess() || self::setError($res['msg'] ?? '发送失败');
    }

    /**
     * 设置header.
     * @return string[]
     */
    private function getHeader(): array
    {
        return [
            'Content-Type' => 'application/json; charset=utf-8',
        ];
    }

    /**
     * 设置token参数.
     * @param string $url 地址
     * @return string
     */
    private function setTokenUrl(string $url = ''): string
    {
        return $url . '?access_token=' . $this->token;
    }

    /**
     * 设置token参数.
     * @return string[]
     */
    private function getNewHeader(): array
    {
        return [
            'x-acs-dingtalk-access-token' => $this->token,
            'Content-Type' => 'application/json; charset=utf-8',
        ];
    }

    /**
     * 设置token方法.
     * @return true
     * @throws Exception
     */
    private function setToken(): bool
    {
        $redis = Cache::store('redis')->handler();
        $redisKey = $redis->get(DingTalkEnum::TOKEN_KEY);
        if (! $redisKey) {
            $this->token = self::getAccessToken();
        }
        $this->token = $redis->get(DingTalkEnum::TOKEN_KEY);
        return true;
    }
}
